1. Data preprocessing
  1. Read FlowJo files into R.
  2. Create a data frame with intensity measurements for each marker for all samples within the experiment to be analyzed.
  3. Harmonize data
  4. Explore clusters
# load necessary libraries 
library(Seurat)
library(dplyr) 
library(ggplot2)
# library(CelltypeR)

Read in the flow data This data should be the gated live cells.
All samples need to be in one folder.


input_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/FlowDataFiles/9MBO"
output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/"

# 1.a Read in FlowJo Files 

# choose to downsample to 9000 where possible
# check this - I don't think it worked
flowset <- fsc_to_fs(input_path, downsample = 9000)
# down sample can be a number, 'none' or 'min'

# look at file names and rename with shorter sample names

sampleNames(flowset)
[1] "2020-03-06- export_bioinfo_3450c_live cells.fcs"      
[2] "2020-03-06- export_bioinfo_AIW_live cells.fcs"        
[3] "2020-03-06- export_bioinfo_AJG_live cells.fcs"        
[4] "2020-03-17- export_bioinfo_old 3450c_live cells.fcs"  
[5] "2020-03-17- export_bioinfo_old AIW_live cells.fcs"    
[6] "2020-03-17- export_bioinfo_old AJG_live cells.fcs"    
[7] "2020-03-17- export_bioinfo_young 3450c_live cells.fcs"
[8] "2020-03-17- export_bioinfo_young AIW_live cells.fcs"  
[9] "2020-03-17- export_bioinfo_young AJG_live cells.fcs"  
sampleNames(flowset) <- sampleNames(flowset) <- c("3450_0306","AIW002_0306","AJG001C_0306","3450_0317A","AIW002_0317A","AJG001C_0317A","3450_0317B","AIW002_0317B","AJG001C_0317B")
sampleNames(flowset)
[1] "3450_0306"     "AIW002_0306"   "AJG001C_0306"  "3450_0317A"   
[5] "AIW002_0317A"  "AJG001C_0317A" "3450_0317B"    "AIW002_0317B" 
[9] "AJG001C_0317B"
# if we want to save the flowset object now we can 
# this would be read back in with flowset 
# 

Harmonize data to remove batch or technical variation

This requires us to look and see where there are two peaks to align. We need to visualize the peaks of the transformed data before aligning.


# we can decided what level of processing to choose with the argument 'processing'
# biexp only applies a biexponential transformation
# align applies biexp transform and then aligns peaks
# retro (default), transforms, aligns and then reverse transforms
flowset_biexp <- harmonize(flowset, processing = 'biexp')
# we can visualize the peaks to see where there are two peaks for alignment

# we need to enter the column index for which peaks to align, the alignment for one or two peaks is not the same. 
#plotdensity_flowset(flowset)
#plotdensity_flowset(flowset_biexp) # to see the peaks

flowset_align <- harmonize(flowset, processing = 'align', 
                           two_peaks = c(7:20),
                       one_peak = c(1:6,21), threshold = 0.01)

Adjusting the distance between landmarks
.........

Adjusting the distance between landmarks
.........
flowset_retro <- harmonize(flowset, processing = 'retro', 
                           two_peaks = c(7:20),
                       one_peak = c(1:6,21), threshold = 0.01)

Adjusting the distance between landmarks
.........

Adjusting the distance between landmarks
.........
df <- flowset_to_csv(flowset_retro)

Now we have made all the different processing of the fsc files. We can visualize the intensity in cell density plots to see the alignment

#plotdensity_flowset(flowset)
plotdensity_flowset(flowset_biexp)
Warning in melt(lapply(as.list(flowset@frames), function(x) { :
  The melt generic in data.table has been passed a list and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(lapply(as.list(flowset@frames), function(x) {    x = as.data.frame(x@exprs)})). In the next version, this warning will become an error.
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
Warning in geom_density_ridges(alpha = 0.4, verbose = FALSE) :
  Ignoring unknown parameters: `verbose`
Picking joint bandwidth of 0.0468
Picking joint bandwidth of 0.0299
Picking joint bandwidth of 0.0164
Picking joint bandwidth of 0.082
Picking joint bandwidth of 0.0669
Picking joint bandwidth of 0.0172
Picking joint bandwidth of 0.818
Picking joint bandwidth of 0.149
Picking joint bandwidth of 0.722
Picking joint bandwidth of 0.862
Picking joint bandwidth of 0.255
Picking joint bandwidth of 0.24
Picking joint bandwidth of 0.157
Picking joint bandwidth of 0.381
Picking joint bandwidth of 0.614
Picking joint bandwidth of 0.729
Picking joint bandwidth of 0.564
Picking joint bandwidth of 0.241
Picking joint bandwidth of 0.754
Picking joint bandwidth of 0.661
Picking joint bandwidth of 0.102

plotdensity_flowset(flowset_align)
Warning in melt(lapply(as.list(flowset@frames), function(x) { :
  The melt generic in data.table has been passed a list and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(lapply(as.list(flowset@frames), function(x) {    x = as.data.frame(x@exprs)})). In the next version, this warning will become an error.
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
Warning in geom_density_ridges(alpha = 0.4, verbose = FALSE) :
  Ignoring unknown parameters: `verbose`
Picking joint bandwidth of 0.0468
Picking joint bandwidth of 0.0299
Picking joint bandwidth of 0.0164
Picking joint bandwidth of 0.082
Picking joint bandwidth of 0.0669
Picking joint bandwidth of 0.0172
Picking joint bandwidth of 0.819
Picking joint bandwidth of 0.194
Picking joint bandwidth of 0.724
Picking joint bandwidth of 0.863
Picking joint bandwidth of 0.301
Picking joint bandwidth of 0.273
Picking joint bandwidth of 0.156
Picking joint bandwidth of 0.39
Picking joint bandwidth of 0.616
Picking joint bandwidth of 0.742
Picking joint bandwidth of 0.561
Picking joint bandwidth of 0.241
Picking joint bandwidth of 0.763
Picking joint bandwidth of 0.657
Picking joint bandwidth of 0.102

#plotdensity_flowset(flowset_retro)

Now we will make test out clustering. For Seurat clustering and Phenograph we will make the Seurat object Flowsome takes in the dataframe directly.


# the function make_seu will take in the df of expression and Antibody/Marker list as a vector and create a seurat object. Values are scaled. Marker expression will be in the "RNA" slot. PCA is calculated using AB vector as the features 

# make sure to always keep the same antibody order or your labels will not be correct


# antibody features in order to appear on the plots
AB <- c("CD24","CD56","CD29","CD15","CD184","CD133","CD71","CD44","GLAST","AQP4","HepaCAM", "CD140a","O4")
seu <- make_seu(df, AB_vector = AB)
Warning: Using an external vector in selections was deprecated in tidyselect
1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
  # Was:
  data %>% select(AB_vector)

  # Now:
  data %>% select(all_of(AB_vector))

See
<https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning
was generated.
Centering and scaling data matrix

  |                                                                  
  |                                                            |   0%
  |                                                                  
  |============================================================| 100%
Warning in irlba(A = t(x = object), nv = npcs, ...) :
  You're computing too large a percentage of total singular values, use a standard svd instead.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
PC_ 1 
Positive:  CD44, CD184, CD24, CD56, CD15, CD133 
Negative:  CD140a, O4, CD29, CD71, HepaCAM, AQP4 
PC_ 2 
Positive:  CD29, CD44, CD140a, CD184, CD71, O4 
Negative:  CD15, CD56, CD24, GLAST, CD133, AQP4 
PC_ 3 
Positive:  CD56, CD29, CD133, CD24, AQP4, GLAST 
Negative:  CD44, CD184, CD140a, O4, HepaCAM, CD15 
PC_ 4 
Positive:  CD133, AQP4, CD184, HepaCAM, CD71, GLAST 
Negative:  CD56, CD24, CD15, CD44, CD29, CD140a 
PC_ 5 
Positive:  CD184, CD24, CD29, CD140a, O4, HepaCAM 
Negative:  CD44, CD133, CD56, CD71, AQP4, CD15 

Save dataframe and seurat object for later


output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/"

# to save the df for later
# write.csv(df, "pathway/filename.csv")
#write.csv(df, paste(output_path,"df9000Feb15.csv", sep = ""))
write.csv(df, paste(output_path,"df9000Feb24.csv", sep = ""))
# save the seurat object
saveRDS(seu, paste(output_path,"seu9000Feb24.RDS", sep = ""))

Read in the csv of the flow files processed and the seurat object

df.input <- read.csv("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/df9000Feb15.csv")

seu <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/seu9000Feb15.RDS")

Test Flowsom


output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/exp_clusters/flow/"

flowsom.test <- flowsom(input = seu, #seurat
                    df_input = df.input, #the processed df2 file before being converted to seurat
                    flow_k = c(3,5),
                    run.stats = TRUE,
                    run.plot = FALSE,
                    save_to = output_path)
Error in h(simpleError(msg, call)) : 
  error in evaluating the argument 'x' in selecting a method for function 'nrow': unused argument (where(is.numeric))

Explore cluster parameters

df2 <- df.input %>% dplyr::select(AB) # need to add this line into the main explore param function 

Phenograph


output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/exp_clusters/pheno/"

pheno.test <- phenograph(input = seu, df_input = df.input,
                         pheno_lou_kn = c(20,40),
                         run.stats = TRUE,
                         run.plot = TRUE,
                         save_to = output_path)
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Computing nearest neighbor graph
Computing SNN
11:41:14 UMAP embedding parameters a = 0.9922 b = 1.112
11:41:14 Read 197160 rows and found 12 numeric columns
11:41:14 Using Annoy for neighbor search, n_neighbors = 20
11:41:14 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
11:41:29 Writing NN index file to temp file /var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//RtmpFmyVpn/file178c93f4deb41
11:41:29 Searching Annoy index using 1 thread, search_k = 2000
11:42:23 Annoy recall = 100%
11:42:24 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 20
11:42:31 Initializing from normalized Laplacian + noise (using irlba)
11:42:44 Commencing optimization for 200 epochs, with 5499496 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
11:44:32 Optimization finished
Run Rphenograph starts:
  -Input data of 197160 rows and 13 columns
  -k is set to 20
  Finding nearest neighbors...DONE ~ 51.131 s
  Compute jaccard coefficient between nearest-neighbor sets...DONE ~ 16.735 s
  Build undirected graph from the weighted links...DONE ~ 19.493 s
  Run louvain clustering on the graph ...DONE ~ 21.039 s
Run Rphenograph DONE, totally takes 108.397999999986s.
  Return a community class
  -Modularity value: 0.8848982 
  -Number of clusters: 42
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Error in select(., where(is.numeric)) : 
  unused argument (where(is.numeric))

Test functions Louvain


output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/exp_clusters/seu/"

lou.test <- louvain(input = seu, #seu object
                    df_input = df.input,
                    pheno_lou_kn = c(20,40),
                    resolutions = c(0.2,0.5),
                    run.plot = FALSE, #option to save the graphs  
                    run.stats = FALSE, #option to save stats list  
                    save_to #only required when save is TRUE
)
Computing nearest neighbor graph
Computing SNN
12:58:17 UMAP embedding parameters a = 0.9922 b = 1.112
12:58:17 Read 197160 rows and found 12 numeric columns
12:58:17 Using Annoy for neighbor search, n_neighbors = 20
12:58:17 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
12:58:31 Writing NN index file to temp file /var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//RtmpFmyVpn/file178c9321b124b
12:58:32 Searching Annoy index using 1 thread, search_k = 2000
12:59:25 Annoy recall = 100%
12:59:26 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 20
12:59:32 Initializing from normalized Laplacian + noise (using irlba)
12:59:44 Commencing optimization for 200 epochs, with 5499496 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:01:31 Optimization finished
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 197160
Number of edges: 5227227

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9166
Number of communities: 14
Elapsed time: 110 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 197160
Number of edges: 5227227

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8845
Number of communities: 22
Elapsed time: 140 seconds
Computing nearest neighbor graph
Computing SNN
13:07:10 UMAP embedding parameters a = 0.9922 b = 1.112
13:07:10 Read 197160 rows and found 12 numeric columns
13:07:10 Using Annoy for neighbor search, n_neighbors = 40
13:07:10 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:07:25 Writing NN index file to temp file /var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//RtmpFmyVpn/file178c9f4e13a0
13:07:25 Searching Annoy index using 1 thread, search_k = 4000
13:08:56 Annoy recall = 100%
13:08:57 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 40
13:09:08 Initializing from normalized Laplacian + noise (using irlba)
13:09:32 Commencing optimization for 200 epochs, with 11184324 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
13:11:50 Optimization finished
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 197160
Number of edges: 11735573

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9134
Number of communities: 12
Elapsed time: 212 seconds
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 197160
Number of edges: 11735573

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8785
Number of communities: 21
Elapsed time: 197 seconds

Annotate clusters 1. Visualization for manual annotation. - output by clustering function 2. CAM (Correlation assignment model) - requires reference matrix 3. RFM (Random Forest Model) - requires annotated matching flow dataset 4. Seurat label transfer - requires annotated matching flow data in a seurat object


# run clustering with only desired conditions - use function - TEMP run directly 

seu <- RunPCA(seu, features = AB)
Warning in irlba(A = t(x = object), nv = npcs, ...) :
  You're computing too large a percentage of total singular values, use a standard svd instead.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
Warning: Requested number is larger than the number of available items (13). Setting to 13.
PC_ 1 
Positive:  CD44, CD184, CD24, CD56, CD15, CD133 
Negative:  CD140a, O4, CD29, CD71, HepaCAM, AQP4 
PC_ 2 
Positive:  CD29, CD44, CD140a, CD184, CD71, O4 
Negative:  CD15, CD56, CD24, GLAST, CD133, AQP4 
PC_ 3 
Positive:  CD56, CD29, CD133, CD24, AQP4, GLAST 
Negative:  CD44, CD184, CD140a, O4, HepaCAM, CD15 
PC_ 4 
Positive:  CD133, AQP4, CD184, HepaCAM, CD71, GLAST 
Negative:  CD56, CD24, CD15, CD44, CD29, CD140a 
PC_ 5 
Positive:  CD184, CD24, CD29, CD140a, O4, HepaCAM 
Negative:  CD44, CD133, CD56, CD71, AQP4, CD15 
seu <- FindNeighbors(seu, dims = 1:12, k.param = 60)
Computing nearest neighbor graph
Computing SNN
# must take one less than the number of antibodies 

seu <- FindClusters(seu, resolution = 0.8)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 73578
Number of edges: 6276606

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8455
Number of communities: 22
Elapsed time: 54 seconds
seu <- RunUMAP(seu, dims = 1:12, n.neighbors = 46, min.dist = 0.4,
               spread = 1.5)
16:49:43 UMAP embedding parameters a = 0.4502 b = 1.076
16:49:43 Read 73578 rows and found 12 numeric columns
16:49:43 Using Annoy for neighbor search, n_neighbors = 46
16:49:43 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:49:49 Writing NN index file to temp file /var/folders/k4/khtkczkd5tn732ftjpwgtr240000gn/T//Rtmpriq53r/file731c697bec6f
16:49:49 Searching Annoy index using 1 thread, search_k = 4600
16:50:22 Annoy recall = 100%
16:50:23 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 46
16:50:29 Initializing from normalized Laplacian + noise (using irlba)
16:50:39 Commencing optimization for 200 epochs, with 4811862 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
16:51:33 Optimization finished
DimPlot(seu)

NA
NA
# save with graph
saveRDS(seu,"/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/seu9000Feb24.RDS")

Visualize expression on UMAP and with heat maps


# this will let us see one at at time
for (i in AB) {
  print(FeaturePlot(seu, features = i, min.cutoff = 'q1', max.cutoff = 'q95', label = TRUE))
}
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`
Rasterizing points since number of points exceeds 100,000.
To disable this behavior set `raster=FALSE`

Some more visualization of expression values


# summary heat map
# use function plotmean

length(unique(seu$RNA_snn_res.0.8))
[1] 22
# there are 22 clusters

cluster.num <- c(0:21)

plotmean(plot_type = 'heatmap',seu = seu, group = 'RNA_snn_res.0.8', markers = AB, 
                     var_names = cluster.num, slot = 'scale.data', xlab = "Cluster",
         ylab = "Antibody")

NA
NA

output_path <- "/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/"
saveRDS(seu, paste(output_path,"cluster9000.RDS"))

Predict cell annotations with CAM (Corralations assignment method)


reference_path <- "/Users/rhalenathomas/GITHUB/CelltypeR/Data/ReferenceMatrix9celltypesOrdered.csv"



test_data <- read.csv("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/exp_clusters/df9000.csv") 
reference_data <- read.csv(reference_path)

cor <- find_correlation(test_data, 
                             reference_data, 
                             min_corr = 0.5, 
                             min_diff = 0.05)

# creates a dataframe with cor1 cor2 and predicted cell type label

Visualize the CAM results


plot_corr(cor)
Warning in melt(df) :
  The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(df). In the next version, this warning will become an error.
Using X, best.cell.type, second.cell.type, cell.label as id variables
Warning in melt(df.downsample) :
  The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(df.downsample). In the next version, this warning will become an error.
Using X, best.cell.type, second.cell.type, cell.label as id variables
Warning in melt(double.cells) :
  The melt generic in data.table has been passed a data.frame and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(double.cells). In the next version, this warning will become an error.
Using X, best.cell.type, second.cell.type, cell.label as id variables
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]
Warning: Removed 35 rows containing missing values (`geom_line()`).
Warning: Removed 35 rows containing missing values (`geom_point()`).

Apply correlation predictions to clusters and output a vector for annotation functions

unique(seu$cor.labels)
 [1] "unknown"               "RG"                    "StemCell"             
 [4] "Oligo"                 "Neuron"                "StemCell-Neuron"      
 [7] "OPC"                   "Endothelial"           "Neuron-StemCell"      
[10] "Endothelial-RG"        "NPC"                   "Neuron-NPC"           
[13] "NPC-StemCell"          "RG-Endothelial"        "RG-Astrocyte"         
[16] "NPC-Neuron"            "Astrocyte"             "OPC-Neuron"           
[19] "StemCell-NPC"          "Neuron-OPC"            "OPC-Oligo"            
[22] "Astrocyte-RG"          "Oligo-OPC"             "Endothelial-Astrocyte"
[25] "OPC-StemCell"          "StemCell-OPC"          "Oligo-NPC"            
[28] "NPC-OPC"               "Oligo-Neuron"          "NPC-Oligo"            
[31] "OPC-NPC"               "Oligo-StemCell"        "StemCell-Oligo"       

Run get annotations function to return a vector of annotation in the order of the clusters.

length(cor.ann$CAM)
[1] 22

Use a trained Random Forest model to predict cell types. Training of the Random Forest model with an annotated data set is below.


# you must have a saved trained model from a data object annotated from the same markers

rf <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/PaperFigures/RFM_trained.11072022.Rds")


rfm.pred <- RFM_predict(seu, rf)
head(rfm.pred)

# add the predictions into the seurat object

seu <- AddMetaData(object=seu, metadata=rfm.pred$`predict(rf, df)`, col.name = 'rfm.labels')

# check that the data is added 
table(seu$rfm.labels)

Get the annotation by cluster for the RFM


rfm.ann <- get_annotation(seu, seu$RNA_snn_res.0.8,seu$rfm.labels, 
               top_n = 3, filter_out = c("unknown","Unknown","Mixed","Mix"), Label = "RFM")
rfm.ann

#rfm.ann <- get_annotation(seu, seu$RNA_snn_res.0.8,seu$rfm.labels, 
 #              top_n = 3, filter_out = FALSE, Label = "RFM")
rfm.ann
dim(rfm.ann)

Plot RFM predictions



plot_lab_clust(seu, seu.cluster = seu$RNA_snn_res.0.8, seu.labels = seu$rfm.labels, filter_out = c("unknown","Unknown","Mixed"))

Predicting cell types with Seurat label transfer using anchors


# takes in a seurat object with the labels added 
# makes a dataframe with the count of predicted labels for each cluster
# input seurat object with the predicted labels in the meta data
# input the clusters meta data slot to be labels
# input the meta data slot with the labels (correlation, random forest, seurat predicted)

#need reference data object with labels
seu.r<- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/PaperFigures/Seu9000annot.08072021.RDS")


# the output is a seurat object with the predicted annotations

seu <- seurat_predict(seu, seu.r, ref_id = 'subgroups', down.sample = 500, markers = AB)

# plot the seurat anchor predictions
# get the annotation table for the seurat anchor predictions 

plot_lab_clust(seu, seu$RNA_snn_res.0.8, seu$seu.pred)

# to not filter anything use c()
seu.ann <- get_annotation(seu, seu$RNA_snn_res.0.8,seu$seu.pred, 
               top_n = 3, filter_out = c(), Label = "Seurat")
seu.ann

Get a consensus of cluster annotations, Add the annotations to the seurat object


ann.list <- list(man.ann,df.cor.ann,rfm.ann,seu.ann)

# annotate the seurat object

seu <- cluster_annotate(seu, ann.list, 
                        annotation_name ="CellType", 
                        to_label = "RNA_snn_res.0.8")


DimPlot(seu, group.by = "CellType")

NA
NA

Just use the annotate functions


seu <- annotate(seu, annotations = man.ann$manual, to_label = "RNA_snn_res.0.8", annotation_name = "MyCellType")

DimPlot(seu, group.by = "MyCellType")

#save with the annotations
saveRDS(seu,"/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/seu9000Feb24.RDS")

Compare groups


seu <- readRDS("/Users/rhalenathomas/Documents/Data/FlowCytometry/PhenoID/Analysis/testingLibrary/seu9000Feb24.RDS")

Add the variables into the seurat object

Genotype <- c("3450","3450","3450","AIW002","AIW002","AIW002","AJG001C","AJG001C","AJG001C")
ExDate <- c("0306","0317","0317","0306","0317","0317","0306","0317","0317")
Batch <- c("B","B","A","B","B","A","B","B","A")
Age <- c("273","284","263","273","284","263","273","284","263")

# Genotype
Idents(seu) <- "Sample"
cluster.ids <- Genotype
# vector with the new names - you need this vector from me

names(cluster.ids) <- levels(seu)    # get the levels
seu <- RenameIdents(seu, cluster.ids) # rename  
seu$Genotype <- Idents(seu)   # add a new dataslot

# Experiment date
Idents(seu) <- "Sample"
cluster.ids <- ExDate
# vector with the new names - you need this vector from me

names(cluster.ids) <- levels(seu)    # get the levels
seu <- RenameIdents(seu, cluster.ids) # rename  
seu$ExDate <- Idents(seu)   # add a new dataslot

# Batch
Idents(seu) <- "Sample"
cluster.ids <- Batch
# vector with the new names - you need this vector from me

names(cluster.ids) <- levels(seu)    # get the levels
seu <- RenameIdents(seu, cluster.ids) # rename  
seu$Batch <- Idents(seu)   # add a new dataslot

# days in final differentiation media
Idents(seu) <- "Sample"
cluster.ids <- Age
# vector with the new names - you need this vector from me

names(cluster.ids) <- levels(seu)    # get the levels
seu <- RenameIdents(seu, cluster.ids) # rename  
seu$DaysinCulture <- Idents(seu)   # add a new dataslot

Plots some variables

# one plot
proportionplots(seu.q,seu.var = seu$Genotype, seu.lable = seu$CellType, groups = "Genotype")
[1] "Number of colours needed17"
[1] "Number of colours entered 1"
[1] "Default ggplot colours used"

# add colours
colours <- c("chocolate1","chocolate3","orange",
                   "lightsalmon", "pink","lightpink3",
                   "steelblue3","deepskyblue",
                   "plum3","purple",
                   "seagreen3","tomato4","burlywood3","grey90","brown",
             "royalblue3", "tan4","yellowgreen")

proportionplots(seu.q,seu.var = seu$Genotype, seu.lable = seu$CellType, groups = "Genotype", my_colours = colours)
[1] "Number of colours needed17"
[1] "Number of colours entered 18"
[1] "Custome colours used."

var.list <- list(seu$DaysinCulture,seu$Batch,seu$ExDate,seu$Genotype)
varnames <- c("Days in Culture", "Batch", "Experiment Date", "Genotype")
# plot all the variables of interest at once

plotproportions(seu, var.list = var.list, xgroup = seu$CellType, varnames = varnames, my_colours = c("blue","red"))
[1] "Number of colours needed17"
[1] "Number of colours entered 2"
[1] "Default ggplot colours used"
[1] "Number of colours needed17"
[1] "Number of colours entered 2"
[1] "Default ggplot colours used"
[1] "Number of colours needed17"
[1] "Number of colours entered 2"
[1] "Default ggplot colours used"
[1] "Number of colours needed17"
[1] "Number of colours entered 2"
[1] "Default ggplot colours used"

Heatmaps


# make sure the order is correct
celltypes <- c("unknown","radial glia 1", "astrocytes 1", "mixed","neurons 1",
               "neurons 2", "epithelial", "astrocytes mature", "npc", "radial glia 2",
               "radial glia 3", "endothelial","neurons 3", "astrocytes 2",
               "oligodendrocytes", "stem-like 1","neural stem")

plotmean(plot_type = 'heatmap',seu = seu, group = 'CellType', markers = AB, 
                     var_names = celltypes, slot = 'scale.data', xlab = "Cell Type",
         ylab = "Antibody")

NA
NA
# dot plot

og.order <- c("unknown","radial glia 1", "astrocytes 1", "mixed","neurons 1",
               "neurons 2", "epithelial", "astrocytes mature", "npc", "radial glia 2",
               "radial glia 3", "endothelial","neurons 3", "astrocytes 2",
               "oligodendrocytes", "stem-like 1","neural stem")

# make sure the terms are exactly the same and you don't miss any
new.order <- c("astrocytes 1","astrocytes 2","astrocytes mature",
               "endothelial","epithelial", "mixed","neurons 1",
               "neurons 2","neurons 3","neural stem","npc",
               "oligodendrocytes",
               "radial glia 1","radial glia 2","radial glia 3","stem-like 1",
               "unknown")
new.order <- rev(new.order)

AB.order <- c("CD24","CD56","CD29","CD15","CD184","CD133","CD71","CD44","GLAST","AQP4","HepaCAM", "CD140a","O4")

plotmean(plot_type = 'dotplot',seu = seu, group = 'CellType', markers = AB, 
                     var_names = celltypes, slot = 'scale.data', xlab = "Cell Type",
         ylab = "Antibody", var1order = new.order, var2order = AB.order)

NA
NA

Statistics comparing groups


# prepare a dataframe for stats
# this function takes the annotated seurat object with all the variables already existing as metadata slots

# check what meta data slots exist in your object
colnames(seu@meta.data)
 [1] "orig.ident"      "nCount_RNA"      "nFeature_RNA"    "Sample"         
 [5] "RNA_snn_res.0.8" "seurat_clusters" "cor.labels"      "rfm.labels"     
 [9] "seu.pred"        "CellType"        "MyCellType"      "Genotype"       
[13] "ExDate"          "Batch"           "DaysinCulture"  

Train Random Forest model Requires a labelled seurat object More cells will give higher accuracy but increase computation time. Run in HPC with lots of cells


rf <- RFM_train(seurate_object = seu, 
                             AB_list = AB, annotations = seu$CellType3,
                      split = c(0.8,0.2),
                      downsample = 20000,
                      seed = 222,
                      mytry = c(1:10),
                      maxnodes = c(12: 25),
                      trees = c(250, 500, 1000,2000),
                      start_node = 15)

save(rf, output_path,"trainedRFMFeb14.Rds")
LS0tCnRpdGxlOiAiQ2VsbHR5cGVSIHdvcmtmbG93IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoxLiBEYXRhIHByZXByb2Nlc3NpbmcKYS4JUmVhZCBGbG93Sm8gZmlsZXMgaW50byBSLgpiLglDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggaW50ZW5zaXR5IG1lYXN1cmVtZW50cyBmb3IgZWFjaCBtYXJrZXIgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgZXhwZXJpbWVudCB0byBiZSBhbmFseXplZC4gIApjLglIYXJtb25pemUgZGF0YSAKZC4JRXhwbG9yZSBjbHVzdGVycwoKYGBge3J9CiMgbG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzIApsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikgCmxpYnJhcnkoZ2dwbG90MikKIyBsaWJyYXJ5KENlbGx0eXBlUikKCmBgYAoKClJlYWQgaW4gdGhlIGZsb3cgZGF0YQpUaGlzIGRhdGEgc2hvdWxkIGJlIHRoZSBnYXRlZCBsaXZlIGNlbGxzLiAgCkFsbCBzYW1wbGVzIG5lZWQgdG8gYmUgaW4gb25lIGZvbGRlci4KCgpgYGB7cn0KCmlucHV0X3BhdGggPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9GbG93RGF0YUZpbGVzLzlNQk8iCm91dHB1dF9wYXRoIDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvdGVzdGluZ0xpYnJhcnkvIgoKIyAxLmEgUmVhZCBpbiBGbG93Sm8gRmlsZXMgCgojIGNob29zZSB0byBkb3duc2FtcGxlIHRvIDkwMDAgd2hlcmUgcG9zc2libGUKIyBjaGVjayB0aGlzIC0gSSBkb24ndCB0aGluayBpdCB3b3JrZWQKZmxvd3NldCA8LSBmc2NfdG9fZnMoaW5wdXRfcGF0aCwgZG93bnNhbXBsZSA9IDkwMDApCiMgZG93biBzYW1wbGUgY2FuIGJlIGEgbnVtYmVyLCAnbm9uZScgb3IgJ21pbicKCiMgbG9vayBhdCBmaWxlIG5hbWVzIGFuZCByZW5hbWUgd2l0aCBzaG9ydGVyIHNhbXBsZSBuYW1lcwoKc2FtcGxlTmFtZXMoZmxvd3NldCkKc2FtcGxlTmFtZXMoZmxvd3NldCkgPC0gc2FtcGxlTmFtZXMoZmxvd3NldCkgPC0gYygiMzQ1MF8wMzA2IiwiQUlXMDAyXzAzMDYiLCJBSkcwMDFDXzAzMDYiLCIzNDUwXzAzMTdBIiwiQUlXMDAyXzAzMTdBIiwiQUpHMDAxQ18wMzE3QSIsIjM0NTBfMDMxN0IiLCJBSVcwMDJfMDMxN0IiLCJBSkcwMDFDXzAzMTdCIikKc2FtcGxlTmFtZXMoZmxvd3NldCkKCgojIGlmIHdlIHdhbnQgdG8gc2F2ZSB0aGUgZmxvd3NldCBvYmplY3Qgbm93IHdlIGNhbiAKIyB0aGlzIHdvdWxkIGJlIHJlYWQgYmFjayBpbiB3aXRoIGZsb3dzZXQgCiMgCgoKCmBgYAoKCkhhcm1vbml6ZSBkYXRhIHRvIHJlbW92ZSBiYXRjaCBvciB0ZWNobmljYWwgdmFyaWF0aW9uCgpUaGlzIHJlcXVpcmVzIHVzIHRvIGxvb2sgYW5kIHNlZSB3aGVyZSB0aGVyZSBhcmUgdHdvIHBlYWtzIHRvIGFsaWduLiBXZSBuZWVkIHRvIHZpc3VhbGl6ZSB0aGUgcGVha3Mgb2YgdGhlIHRyYW5zZm9ybWVkIGRhdGEgYmVmb3JlIGFsaWduaW5nLgoKYGBge3J9CgojIHdlIGNhbiBkZWNpZGVkIHdoYXQgbGV2ZWwgb2YgcHJvY2Vzc2luZyB0byBjaG9vc2Ugd2l0aCB0aGUgYXJndW1lbnQgJ3Byb2Nlc3NpbmcnCiMgYmlleHAgb25seSBhcHBsaWVzIGEgYmlleHBvbmVudGlhbCB0cmFuc2Zvcm1hdGlvbgojIGFsaWduIGFwcGxpZXMgYmlleHAgdHJhbnNmb3JtIGFuZCB0aGVuIGFsaWducyBwZWFrcwojIHJldHJvIChkZWZhdWx0KSwgdHJhbnNmb3JtcywgYWxpZ25zIGFuZCB0aGVuIHJldmVyc2UgdHJhbnNmb3JtcwpmbG93c2V0X2JpZXhwIDwtIGhhcm1vbml6ZShmbG93c2V0LCBwcm9jZXNzaW5nID0gJ2JpZXhwJykKIyB3ZSBjYW4gdmlzdWFsaXplIHRoZSBwZWFrcyB0byBzZWUgd2hlcmUgdGhlcmUgYXJlIHR3byBwZWFrcyBmb3IgYWxpZ25tZW50CgojIHdlIG5lZWQgdG8gZW50ZXIgdGhlIGNvbHVtbiBpbmRleCBmb3Igd2hpY2ggcGVha3MgdG8gYWxpZ24sIHRoZSBhbGlnbm1lbnQgZm9yIG9uZSBvciB0d28gcGVha3MgaXMgbm90IHRoZSBzYW1lLiAKI3Bsb3RkZW5zaXR5X2Zsb3dzZXQoZmxvd3NldCkKI3Bsb3RkZW5zaXR5X2Zsb3dzZXQoZmxvd3NldF9iaWV4cCkgIyB0byBzZWUgdGhlIHBlYWtzCgpmbG93c2V0X2FsaWduIDwtIGhhcm1vbml6ZShmbG93c2V0LCBwcm9jZXNzaW5nID0gJ2FsaWduJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR3b19wZWFrcyA9IGMoNzoyMCksCiAgICAgICAgICAgICAgICAgICAgICAgb25lX3BlYWsgPSBjKDE6NiwyMSksIHRocmVzaG9sZCA9IDAuMDEpCgpmbG93c2V0X3JldHJvIDwtIGhhcm1vbml6ZShmbG93c2V0LCBwcm9jZXNzaW5nID0gJ3JldHJvJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR3b19wZWFrcyA9IGMoNzoyMCksCiAgICAgICAgICAgICAgICAgICAgICAgb25lX3BlYWsgPSBjKDE6NiwyMSksIHRocmVzaG9sZCA9IDAuMDEpCgpkZiA8LSBmbG93c2V0X3RvX2NzdihmbG93c2V0X3JldHJvKQoKCgoKYGBgCgpOb3cgd2UgaGF2ZSBtYWRlIGFsbCB0aGUgZGlmZmVyZW50IHByb2Nlc3Npbmcgb2YgdGhlIGZzYyBmaWxlcy4gIFdlIGNhbiB2aXN1YWxpemUgdGhlIGludGVuc2l0eSBpbiBjZWxsIGRlbnNpdHkgcGxvdHMgdG8gc2VlIHRoZSBhbGlnbm1lbnQKCmBgYHtyfQojcGxvdGRlbnNpdHlfZmxvd3NldChmbG93c2V0KQpwbG90ZGVuc2l0eV9mbG93c2V0KGZsb3dzZXRfYmlleHApCnBsb3RkZW5zaXR5X2Zsb3dzZXQoZmxvd3NldF9hbGlnbikKI3Bsb3RkZW5zaXR5X2Zsb3dzZXQoZmxvd3NldF9yZXRybykKCgpgYGAKCk5vdyB3ZSB3aWxsIG1ha2UgdGVzdCBvdXQgY2x1c3RlcmluZy4KRm9yIFNldXJhdCBjbHVzdGVyaW5nIGFuZCBQaGVub2dyYXBoIHdlIHdpbGwgbWFrZSB0aGUgU2V1cmF0IG9iamVjdApGbG93c29tZSB0YWtlcyBpbiB0aGUgZGF0YWZyYW1lIGRpcmVjdGx5LgoKCmBgYHtyfQoKIyB0aGUgZnVuY3Rpb24gbWFrZV9zZXUgd2lsbCB0YWtlIGluIHRoZSBkZiBvZiBleHByZXNzaW9uIGFuZCBBbnRpYm9keS9NYXJrZXIgbGlzdCBhcyBhIHZlY3RvciBhbmQgY3JlYXRlIGEgc2V1cmF0IG9iamVjdC4gVmFsdWVzIGFyZSBzY2FsZWQuIE1hcmtlciBleHByZXNzaW9uIHdpbGwgYmUgaW4gdGhlICJSTkEiIHNsb3QuIFBDQSBpcyBjYWxjdWxhdGVkIHVzaW5nIEFCIHZlY3RvciBhcyB0aGUgZmVhdHVyZXMgCgojIG1ha2Ugc3VyZSB0byBhbHdheXMga2VlcCB0aGUgc2FtZSBhbnRpYm9keSBvcmRlciBvciB5b3VyIGxhYmVscyB3aWxsIG5vdCBiZSBjb3JyZWN0CgoKIyBhbnRpYm9keSBmZWF0dXJlcyBpbiBvcmRlciB0byBhcHBlYXIgb24gdGhlIHBsb3RzCkFCIDwtIGMoIkNEMjQiLCJDRDU2IiwiQ0QyOSIsIkNEMTUiLCJDRDE4NCIsIkNEMTMzIiwiQ0Q3MSIsIkNENDQiLCJHTEFTVCIsIkFRUDQiLCJIZXBhQ0FNIiwgIkNEMTQwYSIsIk80IikKc2V1IDwtIG1ha2Vfc2V1KGRmLCBBQl92ZWN0b3IgPSBBQikKCgpgYGAKU2F2ZSBkYXRhZnJhbWUgYW5kIHNldXJhdCBvYmplY3QgZm9yIGxhdGVyCgpgYGB7cn0KCm91dHB1dF9wYXRoIDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvdGVzdGluZ0xpYnJhcnkvIgoKIyB0byBzYXZlIHRoZSBkZiBmb3IgbGF0ZXIKIyB3cml0ZS5jc3YoZGYsICJwYXRod2F5L2ZpbGVuYW1lLmNzdiIpCiN3cml0ZS5jc3YoZGYsIHBhc3RlKG91dHB1dF9wYXRoLCJkZjkwMDBGZWIxNS5jc3YiLCBzZXAgPSAiIikpCndyaXRlLmNzdihkZiwgcGFzdGUob3V0cHV0X3BhdGgsImRmOTAwMEZlYjI0LmNzdiIsIHNlcCA9ICIiKSkKIyBzYXZlIHRoZSBzZXVyYXQgb2JqZWN0CnNhdmVSRFMoc2V1LCBwYXN0ZShvdXRwdXRfcGF0aCwic2V1OTAwMEZlYjI0LlJEUyIsIHNlcCA9ICIiKSkKCgoKYGBgCgoKUmVhZCBpbiB0aGUgY3N2IG9mIHRoZSBmbG93IGZpbGVzIHByb2Nlc3NlZCBhbmQgdGhlIHNldXJhdCBvYmplY3QKCmBgYHtyfQpkZi5pbnB1dCA8LSByZWFkLmNzdigiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL3Rlc3RpbmdMaWJyYXJ5L2RmOTAwMEZlYjI0LmNzdiIpCgpzZXUgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL3Rlc3RpbmdMaWJyYXJ5L3NldTkwMDBGZWIyNC5SRFMiKQoKCmBgYAoKVGVzdCBGbG93c29tCgpgYGB7cn0KCm91dHB1dF9wYXRoIDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvdGVzdGluZ0xpYnJhcnkvZXhwX2NsdXN0ZXJzL2Zsb3cvIgoKZmxvd3NvbS50ZXN0IDwtIGZsb3dzb20oaW5wdXQgPSBzZXUsICNzZXVyYXQKICAgICAgICAgICAgICAgICAgICBkZl9pbnB1dCA9IGRmLmlucHV0LCAjdGhlIHByb2Nlc3NlZCBkZjIgZmlsZSBiZWZvcmUgYmVpbmcgY29udmVydGVkIHRvIHNldXJhdAogICAgICAgICAgICAgICAgICAgIGZsb3dfayA9IGMoMyw1KSwKICAgICAgICAgICAgICAgICAgICBydW4uc3RhdHMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIHJ1bi5wbG90ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgc2F2ZV90byA9IG91dHB1dF9wYXRoKQoKYGBgCgoKCkV4cGxvcmUgY2x1c3RlciBwYXJhbWV0ZXJzCgpgYGB7cn0KZGYyIDwtIGRmLmlucHV0ICU+JSBkcGx5cjo6c2VsZWN0KEFCKSAjIG5lZWQgdG8gYWRkIHRoaXMgbGluZSBpbnRvIHRoZSBtYWluIGV4cGxvcmUgcGFyYW0gZnVuY3Rpb24gCgpgYGAKClBoZW5vZ3JhcGgKCmBgYHtyfQoKb3V0cHV0X3BhdGggPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy90ZXN0aW5nTGlicmFyeS9leHBfY2x1c3RlcnMvcGhlbm8vIgoKcGhlbm8udGVzdCA8LSBwaGVub2dyYXBoKGlucHV0ID0gc2V1LCBkZl9pbnB1dCA9IGRmLmlucHV0LAogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9fbG91X2tuID0gYygyMCw0MCksCiAgICAgICAgICAgICAgICAgICAgICAgICBydW4uc3RhdHMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgcnVuLnBsb3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgc2F2ZV90byA9IG91dHB1dF9wYXRoKQoKCmBgYAoKClRlc3QgZnVuY3Rpb25zIExvdXZhaW4KCmBgYHtyfQoKb3V0cHV0X3BhdGggPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy90ZXN0aW5nTGlicmFyeS9leHBfY2x1c3RlcnMvc2V1LyIKCmxvdS50ZXN0IDwtIGxvdXZhaW4oaW5wdXQgPSBzZXUsICNzZXUgb2JqZWN0CiAgICAgICAgICAgICAgICAgICAgZGZfaW5wdXQgPSBkZi5pbnB1dCwKICAgICAgICAgICAgICAgICAgICBwaGVub19sb3Vfa24gPSBjKDIwLDQwKSwKICAgICAgICAgICAgICAgICAgICByZXNvbHV0aW9ucyA9IGMoMC4yLDAuNSksCiAgICAgICAgICAgICAgICAgICAgcnVuLnBsb3QgPSBGQUxTRSwgI29wdGlvbiB0byBzYXZlIHRoZSBncmFwaHMgIAogICAgICAgICAgICAgICAgICAgIHJ1bi5zdGF0cyA9IEZBTFNFLCAjb3B0aW9uIHRvIHNhdmUgc3RhdHMgbGlzdCAgCiAgICAgICAgICAgICAgICAgICAgc2F2ZV90byAjb25seSByZXF1aXJlZCB3aGVuIHNhdmUgaXMgVFJVRQopCgoKYGBgCgoKCgoKQW5ub3RhdGUgY2x1c3RlcnMKMS4gVmlzdWFsaXphdGlvbiBmb3IgbWFudWFsIGFubm90YXRpb24uIC0gb3V0cHV0IGJ5IGNsdXN0ZXJpbmcgZnVuY3Rpb24KMi4gQ0FNIChDb3JyZWxhdGlvbiBhc3NpZ25tZW50IG1vZGVsKSAtIHJlcXVpcmVzIHJlZmVyZW5jZSBtYXRyaXgKMy4gUkZNIChSYW5kb20gRm9yZXN0IE1vZGVsKSAtIHJlcXVpcmVzIGFubm90YXRlZCBtYXRjaGluZyBmbG93IGRhdGFzZXQKNC4gU2V1cmF0IGxhYmVsIHRyYW5zZmVyIC0gcmVxdWlyZXMgYW5ub3RhdGVkIG1hdGNoaW5nIGZsb3cgZGF0YSBpbiBhIHNldXJhdCBvYmplY3QKCgoKYGBge3J9CgojIHJ1biBjbHVzdGVyaW5nIHdpdGggb25seSBkZXNpcmVkIGNvbmRpdGlvbnMgLSB1c2UgZnVuY3Rpb24gLSBURU1QIHJ1biBkaXJlY3RseSAKCnNldSA8LSBSdW5QQ0Eoc2V1LCBmZWF0dXJlcyA9IEFCKQpzZXUgPC0gRmluZE5laWdoYm9ycyhzZXUsIGRpbXMgPSAxOjEyLCBrLnBhcmFtID0gNjApCiMgbXVzdCB0YWtlIG9uZSBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBhbnRpYm9kaWVzIAoKc2V1IDwtIEZpbmRDbHVzdGVycyhzZXUsIHJlc29sdXRpb24gPSAwLjgpCnNldSA8LSBSdW5VTUFQKHNldSwgZGltcyA9IDE6MTIsIG4ubmVpZ2hib3JzID0gNDYsIG1pbi5kaXN0ID0gMC40LAogICAgICAgICAgICAgICBzcHJlYWQgPSAxLjUpCkRpbVBsb3Qoc2V1KQoKCmBgYAoKYGBge3J9CiMgc2F2ZSB3aXRoIGdyYXBoCnNhdmVSRFMoc2V1LCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvdGVzdGluZ0xpYnJhcnkvc2V1OTAwMEZlYjI0LlJEUyIpCgoKCmBgYAoKCgpWaXN1YWxpemUgZXhwcmVzc2lvbiBvbiBVTUFQIGFuZCB3aXRoIGhlYXQgbWFwcwoKYGBge3J9CgojIHRoaXMgd2lsbCBsZXQgdXMgc2VlIG9uZSBhdCBhdCB0aW1lCmZvciAoaSBpbiBBQikgewogIHByaW50KEZlYXR1cmVQbG90KHNldSwgZmVhdHVyZXMgPSBpLCBtaW4uY3V0b2ZmID0gJ3ExJywgbWF4LmN1dG9mZiA9ICdxOTUnLCBsYWJlbCA9IFRSVUUpKQp9CgoKYGBgCgpTb21lIG1vcmUgdmlzdWFsaXphdGlvbiBvZiBleHByZXNzaW9uIHZhbHVlcwoKYGBge3J9CgojIHN1bW1hcnkgaGVhdCBtYXAKIyB1c2UgZnVuY3Rpb24gcGxvdG1lYW4KCmxlbmd0aCh1bmlxdWUoc2V1JFJOQV9zbm5fcmVzLjAuOCkpCiMgdGhlcmUgYXJlIDIyIGNsdXN0ZXJzCgpjbHVzdGVyLm51bSA8LSBjKDA6MjEpCgpwbG90bWVhbihwbG90X3R5cGUgPSAnaGVhdG1hcCcsc2V1ID0gc2V1LCBncm91cCA9ICdSTkFfc25uX3Jlcy4wLjgnLCBtYXJrZXJzID0gQUIsIAogICAgICAgICAgICAgICAgICAgICB2YXJfbmFtZXMgPSBjbHVzdGVyLm51bSwgc2xvdCA9ICdzY2FsZS5kYXRhJywgeGxhYiA9ICJDbHVzdGVyIiwKICAgICAgICAgeWxhYiA9ICJBbnRpYm9keSIpCgoKYGBgCgoKCgoKYGBge3J9CgpvdXRwdXRfcGF0aCA8LSAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL3Rlc3RpbmdMaWJyYXJ5LyIKc2F2ZVJEUyhzZXUsIHBhc3RlKG91dHB1dF9wYXRoLCJjbHVzdGVyOTAwMC5SRFMiKSkKCmBgYAoKUHJlZGljdCBjZWxsIGFubm90YXRpb25zIHdpdGggQ0FNIChDb3JyYWxhdGlvbnMgYXNzaWdubWVudCBtZXRob2QpCgpgYGB7cn0KCnJlZmVyZW5jZV9wYXRoIDwtICIvVXNlcnMvcmhhbGVuYXRob21hcy9HSVRIVUIvQ2VsbHR5cGVSL0RhdGEvUmVmZXJlbmNlTWF0cml4OWNlbGx0eXBlc09yZGVyZWQuY3N2IgoKcmVmZXJlbmNlX2RhdGEgPC0gcmVhZC5jc3YocmVmZXJlbmNlX3BhdGgpCgpjb3IgPC0gZmluZF9jb3JyZWxhdGlvbihkZi5pbnB1dCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVmZXJlbmNlX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9jb3JyID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fZGlmZiA9IDAuMDUpCgojIGNyZWF0ZXMgYSBkYXRhZnJhbWUgd2l0aCBjb3IxIGNvcjIgYW5kIHByZWRpY3RlZCBjZWxsIHR5cGUgbGFiZWwKCgoKYGBgCgpWaXN1YWxpemUgdGhlIENBTSByZXN1bHRzCgpgYGB7cn0KCnBsb3RfY29ycihjb3IpCgoKYGBgCgpBcHBseSBjb3JyZWxhdGlvbiBwcmVkaWN0aW9ucyB0byBjbHVzdGVycyBhbmQgb3V0cHV0IGEgdmVjdG9yIGZvciBhbm5vdGF0aW9uIGZ1bmN0aW9ucwoKYGBge3J9CgoKIyBhZGQgdGhlIGNvcnJlbGF0aW9uIHByZWRpY3Rpb25zIHRvIHRoZSBtZXRhIGRhdGEKc2V1IDwtIEFkZE1ldGFEYXRhKG9iamVjdD1zZXUsIG1ldGFkYXRhPWNvciRjZWxsLmxhYmVsLCBjb2wubmFtZSA9ICdjb3IubGFiZWxzJykKIyBzZWUgdGhlIGxhYmVscyBhZGRlZAp1bmlxdWUoc2V1JGNvci5sYWJlbHMpCgpzZXUuY2x1c3RlciA9IHNldSRSTkFfc25uX3Jlcy4wLjgKc2V1LmxhYmVscyA9IHNldSRjb3IubGFiZWxzCgojIHBsb3QgdGhlIGNsdXN0ZXIgcHJlZGljdGlvbnMKcGxvdF9sYWJfY2x1c3Qoc2V1LCBzZXUkUk5BX3Nubl9yZXMuMC44LCBzZXUkY29yLmxhYmVscykKCgoKCmBgYAoKUnVuIGdldCBhbm5vdGF0aW9ucyBmdW5jdGlvbiB0byByZXR1cm4gYSB2ZWN0b3Igb2YgYW5ub3RhdGlvbiBpbiB0aGUgb3JkZXIgb2YgdGhlIGNsdXN0ZXJzLgoKYGBge3J9Cgpjb3IuYW5uIDwtIGdldF9hbm5vdGF0aW9uKHNldSwgc2V1LmNsdXN0ZXIgPSBzZXUkUk5BX3Nubl9yZXMuMC44LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXUubGFiZWwgPSBzZXUkY29yLmxhYmVscywgdG9wX24gPSAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJfb3V0ID0gYygiVW5rbm93biIsInVua25vd24iLCJNaXhlZCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBMYWJlbCA9ICJDQU0iKQpjb3IuYW5uCmRpbShjb3IuYW5uKQoKY29yLmFubiA8LSBjb3IuYW5uWzE6MjIsXQpkaW0oY29yLmFubikKCnVuaXF1ZShjb3IuYW5uJENBTSkKbGVuZ3RoKGNvci5hbm4kQ0FNKQoKYGBgCgoKCgoKVXNlIGEgdHJhaW5lZCBSYW5kb20gRm9yZXN0IG1vZGVsIHRvIHByZWRpY3QgY2VsbCB0eXBlcy4gClRyYWluaW5nIG9mIHRoZSBSYW5kb20gRm9yZXN0IG1vZGVsIHdpdGggYW4gYW5ub3RhdGVkIGRhdGEgc2V0IGlzIGJlbG93LgoKYGBge3J9CgojIHlvdSBtdXN0IGhhdmUgYSBzYXZlZCB0cmFpbmVkIG1vZGVsIGZyb20gYSBkYXRhIG9iamVjdCBhbm5vdGF0ZWQgZnJvbSB0aGUgc2FtZSBtYXJrZXJzCgpyZiA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9GbG93Q3l0b21ldHJ5L1BoZW5vSUQvQW5hbHlzaXMvUGFwZXJGaWd1cmVzL1JGTV90cmFpbmVkLjExMDcyMDIyLlJkcyIpCgoKcmZtLnByZWQgPC0gUkZNX3ByZWRpY3Qoc2V1LCByZikKaGVhZChyZm0ucHJlZCkKCiMgYWRkIHRoZSBwcmVkaWN0aW9ucyBpbnRvIHRoZSBzZXVyYXQgb2JqZWN0CgpzZXUgPC0gQWRkTWV0YURhdGEob2JqZWN0PXNldSwgbWV0YWRhdGE9cmZtLnByZWQkYHByZWRpY3QocmYsIGRmKWAsIGNvbC5uYW1lID0gJ3JmbS5sYWJlbHMnKQoKIyBjaGVjayB0aGF0IHRoZSBkYXRhIGlzIGFkZGVkIAp0YWJsZShzZXUkcmZtLmxhYmVscykKCgpgYGAKCkdldCB0aGUgYW5ub3RhdGlvbiBieSBjbHVzdGVyIGZvciB0aGUgUkZNCgpgYGB7cn0KCnJmbS5hbm4gPC0gZ2V0X2Fubm90YXRpb24oc2V1LCBzZXUkUk5BX3Nubl9yZXMuMC44LHNldSRyZm0ubGFiZWxzLCAKICAgICAgICAgICAgICAgdG9wX24gPSAzLCBmaWx0ZXJfb3V0ID0gYygidW5rbm93biIsIlVua25vd24iLCJNaXhlZCIsIk1peCIpLCBMYWJlbCA9ICJSRk0iKQpyZm0uYW5uCgojcmZtLmFubiA8LSBnZXRfYW5ub3RhdGlvbihzZXUsIHNldSRSTkFfc25uX3Jlcy4wLjgsc2V1JHJmbS5sYWJlbHMsIAogIyAgICAgICAgICAgICAgdG9wX24gPSAzLCBmaWx0ZXJfb3V0ID0gRkFMU0UsIExhYmVsID0gIlJGTSIpCnJmbS5hbm4KZGltKHJmbS5hbm4pCgpgYGAKClBsb3QgUkZNIHByZWRpY3Rpb25zCgpgYGB7cn0KCgpwbG90X2xhYl9jbHVzdChzZXUsIHNldS5jbHVzdGVyID0gc2V1JFJOQV9zbm5fcmVzLjAuOCwgc2V1LmxhYmVscyA9IHNldSRyZm0ubGFiZWxzLCBmaWx0ZXJfb3V0ID0gYygidW5rbm93biIsIlVua25vd24iLCJNaXhlZCIpKQoKCmBgYAoKCgpQcmVkaWN0aW5nIGNlbGwgdHlwZXMgd2l0aCBTZXVyYXQgbGFiZWwgdHJhbnNmZXIgdXNpbmcgYW5jaG9ycwoKCmBgYHtyfQoKIyB0YWtlcyBpbiBhIHNldXJhdCBvYmplY3Qgd2l0aCB0aGUgbGFiZWxzIGFkZGVkIAojIG1ha2VzIGEgZGF0YWZyYW1lIHdpdGggdGhlIGNvdW50IG9mIHByZWRpY3RlZCBsYWJlbHMgZm9yIGVhY2ggY2x1c3RlcgojIGlucHV0IHNldXJhdCBvYmplY3Qgd2l0aCB0aGUgcHJlZGljdGVkIGxhYmVscyBpbiB0aGUgbWV0YSBkYXRhCiMgaW5wdXQgdGhlIGNsdXN0ZXJzIG1ldGEgZGF0YSBzbG90IHRvIGJlIGxhYmVscwojIGlucHV0IHRoZSBtZXRhIGRhdGEgc2xvdCB3aXRoIHRoZSBsYWJlbHMgKGNvcnJlbGF0aW9uLCByYW5kb20gZm9yZXN0LCBzZXVyYXQgcHJlZGljdGVkKQoKI25lZWQgcmVmZXJlbmNlIGRhdGEgb2JqZWN0IHdpdGggbGFiZWxzCnNldS5yPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL1BhcGVyRmlndXJlcy9TZXU5MDAwYW5ub3QuMDgwNzIwMjEuUkRTIikKCgojIHRoZSBvdXRwdXQgaXMgYSBzZXVyYXQgb2JqZWN0IHdpdGggdGhlIHByZWRpY3RlZCBhbm5vdGF0aW9ucwoKc2V1IDwtIHNldXJhdF9wcmVkaWN0KHNldSwgc2V1LnIsIHJlZl9pZCA9ICdzdWJncm91cHMnLCBkb3duLnNhbXBsZSA9IDUwMCwgbWFya2VycyA9IEFCKQoKCgoKYGBgCgpgYGB7cn0KCiMgcGxvdCB0aGUgc2V1cmF0IGFuY2hvciBwcmVkaWN0aW9ucwojIGdldCB0aGUgYW5ub3RhdGlvbiB0YWJsZSBmb3IgdGhlIHNldXJhdCBhbmNob3IgcHJlZGljdGlvbnMgCgpwbG90X2xhYl9jbHVzdChzZXUsIHNldSRSTkFfc25uX3Jlcy4wLjgsIHNldSRzZXUucHJlZCkKCiMgdG8gbm90IGZpbHRlciBhbnl0aGluZyB1c2UgYygpCnNldS5hbm4gPC0gZ2V0X2Fubm90YXRpb24oc2V1LCBzZXUkUk5BX3Nubl9yZXMuMC44LHNldSRzZXUucHJlZCwgCiAgICAgICAgICAgICAgIHRvcF9uID0gMywgZmlsdGVyX291dCA9IGMoKSwgTGFiZWwgPSAiU2V1cmF0IikKc2V1LmFubgoKCmBgYAoKR2V0IGEgY29uc2Vuc3VzIG9mIGNsdXN0ZXIgYW5ub3RhdGlvbnMsIEFkZCB0aGUgYW5ub3RhdGlvbnMgdG8gdGhlIHNldXJhdCBvYmplY3QKCgoKYGBge3J9CgojIG1hbnVhbCBhbm5vdGF0aW9ucwojIHF1aWNrIGNoZWF0IGp1c3QgdXNpbmcgdGhlIHNldXJhdCBsYWJlbHMgdG8gZ2V0IGEgc3Rpbmcgb2YgYW5ub3RhdGlvbnMKCnRlbXAgPC0gcGFzdGUoIiciLGFzLmNoYXJhY3RlcihzZXUuYW5uJFNldXJhdCksICInIiwgY29sbGFwc2UgPSAiLCAiLCBzZXAgPSAiIiApCgpteV9hbm4gPC0gYygnVW5rbm93bicsICdSYWRpYWwgR2xpYSAxJywgJ0FzdHJvY3l0ZXMgMScsICdNaXhlZCcsICdOZXVyb25zIDEnLCAnTmV1cm9ucyAyJywgJ0VwaXRoZWxpYWwnLCAnQXN0cm9jeXRlcyBtYXR1cmUnLCAnTlBDJywgJ1JhZGlhbCBHbGlhIDInLCAnUmFkaWFsIEdsaWEgMycsICdFbmRvdGhlbGlhbCcsICdFcGl0aGVsaWFsJywgJ05ldXJvbnMgMycsICdOZXVyb25zIDEnLCAnQXN0cm9jeXRlcyAyJywgJ05ldXJvbnMgMScsICdVbmtub3duJywgJ09saWdvZGVuZHJvY3l0ZXMnLCAnU3RlbS1saWtlIDEnLCAnTmV1cmFsIHN0ZW0nLCAnRXBpdGhlbGlhbCcpCmNsdXN0ZXIubiA8LSBjKDA6MjEpCm1hbi5hbm4gPC0gZGF0YS5mcmFtZShjbHVzdGVyLm4sIG15X2FubikKY29sbmFtZXMobWFuLmFubikgPC0gYygiQ2x1c3RlciIsIm1hbnVhbCIpCgojIG5lZWRzIHRvIGJlIGZhY3RvcnMgCm1hbi5hbm4gPC0gZGF0YS5mcmFtZShsYXBwbHkobWFuLmFubiwgZmFjdG9yKSkKCmRpbShtYW4uYW5uKQpkaW0oY29yLmFubikKZGltKHJmbS5hbm4pCmRpbShzZXUuYW5uKQoKIyB0aGUgZGF0YWZyYW1lcyBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aAoKIyB0aGUgYW5ub3RhdGlvbiBmdW5jdGlvbiB0YWtlcyBpbiBhIGxpc3Qgb2YgdGhlIGFubm90YXRpb24gZGF0YWZyYW1lcwoKYW5uLmxpc3QgPC0gbGlzdChtYW4uYW5uLGRmLmNvci5hbm4scmZtLmFubixzZXUuYW5uKQoKIyBhbm5vdGF0ZSB0aGUgc2V1cmF0IG9iamVjdAoKc2V1IDwtIGNsdXN0ZXJfYW5ub3RhdGUoc2V1LCBhbm4ubGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZSA9IkNlbGxUeXBlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRvX2xhYmVsID0gIlJOQV9zbm5fcmVzLjAuOCIpCgpEaW1QbG90KHNldSwgZ3JvdXAuYnkgPSAiQ2VsbFR5cGUiKQoKCmBgYAoKSnVzdCB1c2UgdGhlIGFubm90YXRlIGZ1bmN0aW9ucwoKYGBge3J9CgpzZXUgPC0gYW5ub3RhdGUoc2V1LCBhbm5vdGF0aW9ucyA9IG1hbi5hbm4kbWFudWFsLCB0b19sYWJlbCA9ICJSTkFfc25uX3Jlcy4wLjgiLCBhbm5vdGF0aW9uX25hbWUgPSAiTXlDZWxsVHlwZSIpCgpEaW1QbG90KHNldSwgZ3JvdXAuYnkgPSAiTXlDZWxsVHlwZSIpCgpgYGAKCgoKCgpgYGB7cn0KI3NhdmUgd2l0aCB0aGUgYW5ub3RhdGlvbnMKc2F2ZVJEUyhzZXUsIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy90ZXN0aW5nTGlicmFyeS9zZXU5MDAwRmViMjQuUkRTIikKCgpgYGAKCgpDb21wYXJlIGdyb3VwcwoKCmBgYHtyfQoKc2V1IDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL0Zsb3dDeXRvbWV0cnkvUGhlbm9JRC9BbmFseXNpcy90ZXN0aW5nTGlicmFyeS9zZXU5MDAwRmViMjQuUkRTIikKCgpgYGAKCgpBZGQgdGhlIHZhcmlhYmxlcyBpbnRvIHRoZSBzZXVyYXQgb2JqZWN0CmBgYHtyfQpHZW5vdHlwZSA8LSBjKCIzNDUwIiwiMzQ1MCIsIjM0NTAiLCJBSVcwMDIiLCJBSVcwMDIiLCJBSVcwMDIiLCJBSkcwMDFDIiwiQUpHMDAxQyIsIkFKRzAwMUMiKQpFeERhdGUgPC0gYygiMDMwNiIsIjAzMTciLCIwMzE3IiwiMDMwNiIsIjAzMTciLCIwMzE3IiwiMDMwNiIsIjAzMTciLCIwMzE3IikKQmF0Y2ggPC0gYygiQiIsIkIiLCJBIiwiQiIsIkIiLCJBIiwiQiIsIkIiLCJBIikKQWdlIDwtIGMoIjI3MyIsIjI4NCIsIjI2MyIsIjI3MyIsIjI4NCIsIjI2MyIsIjI3MyIsIjI4NCIsIjI2MyIpCgojIEdlbm90eXBlCklkZW50cyhzZXUpIDwtICJTYW1wbGUiCmNsdXN0ZXIuaWRzIDwtIEdlbm90eXBlCiMgdmVjdG9yIHdpdGggdGhlIG5ldyBuYW1lcyAtIHlvdSBuZWVkIHRoaXMgdmVjdG9yIGZyb20gbWUKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1KSAgICAjIGdldCB0aGUgbGV2ZWxzCnNldSA8LSBSZW5hbWVJZGVudHMoc2V1LCBjbHVzdGVyLmlkcykgIyByZW5hbWUgIApzZXUkR2Vub3R5cGUgPC0gSWRlbnRzKHNldSkgICAjIGFkZCBhIG5ldyBkYXRhc2xvdAoKIyBFeHBlcmltZW50IGRhdGUKSWRlbnRzKHNldSkgPC0gIlNhbXBsZSIKY2x1c3Rlci5pZHMgPC0gRXhEYXRlCiMgdmVjdG9yIHdpdGggdGhlIG5ldyBuYW1lcyAtIHlvdSBuZWVkIHRoaXMgdmVjdG9yIGZyb20gbWUKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1KSAgICAjIGdldCB0aGUgbGV2ZWxzCnNldSA8LSBSZW5hbWVJZGVudHMoc2V1LCBjbHVzdGVyLmlkcykgIyByZW5hbWUgIApzZXUkRXhEYXRlIDwtIElkZW50cyhzZXUpICAgIyBhZGQgYSBuZXcgZGF0YXNsb3QKCiMgQmF0Y2gKSWRlbnRzKHNldSkgPC0gIlNhbXBsZSIKY2x1c3Rlci5pZHMgPC0gQmF0Y2gKIyB2ZWN0b3Igd2l0aCB0aGUgbmV3IG5hbWVzIC0geW91IG5lZWQgdGhpcyB2ZWN0b3IgZnJvbSBtZQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUpICAgICMgZ2V0IHRoZSBsZXZlbHMKc2V1IDwtIFJlbmFtZUlkZW50cyhzZXUsIGNsdXN0ZXIuaWRzKSAjIHJlbmFtZSAgCnNldSRCYXRjaCA8LSBJZGVudHMoc2V1KSAgICMgYWRkIGEgbmV3IGRhdGFzbG90CgojIGRheXMgaW4gZmluYWwgZGlmZmVyZW50aWF0aW9uIG1lZGlhCklkZW50cyhzZXUpIDwtICJTYW1wbGUiCmNsdXN0ZXIuaWRzIDwtIEFnZQojIHZlY3RvciB3aXRoIHRoZSBuZXcgbmFtZXMgLSB5b3UgbmVlZCB0aGlzIHZlY3RvciBmcm9tIG1lCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldSkgICAgIyBnZXQgdGhlIGxldmVscwpzZXUgPC0gUmVuYW1lSWRlbnRzKHNldSwgY2x1c3Rlci5pZHMpICMgcmVuYW1lICAKc2V1JERheXNpbkN1bHR1cmUgPC0gSWRlbnRzKHNldSkgICAjIGFkZCBhIG5ldyBkYXRhc2xvdAoKCgpgYGAKClBsb3RzIHNvbWUgdmFyaWFibGVzCgpgYGB7cn0KIyBvbmUgcGxvdApwcm9wb3J0aW9ucGxvdHMoc2V1LnEsc2V1LnZhciA9IHNldSRHZW5vdHlwZSwgc2V1LmxhYmxlID0gc2V1JENlbGxUeXBlLCBncm91cHMgPSAiR2Vub3R5cGUiKQojIGFkZCBjb2xvdXJzCmNvbG91cnMgPC0gYygiY2hvY29sYXRlMSIsImNob2NvbGF0ZTMiLCJvcmFuZ2UiLAogICAgICAgICAgICAgICAgICAgImxpZ2h0c2FsbW9uIiwgInBpbmsiLCJsaWdodHBpbmszIiwKICAgICAgICAgICAgICAgICAgICJzdGVlbGJsdWUzIiwiZGVlcHNreWJsdWUiLAogICAgICAgICAgICAgICAgICAgInBsdW0zIiwicHVycGxlIiwKICAgICAgICAgICAgICAgICAgICJzZWFncmVlbjMiLCJ0b21hdG80IiwiYnVybHl3b29kMyIsImdyZXk5MCIsImJyb3duIiwKICAgICAgICAgICAgICJyb3lhbGJsdWUzIiwgInRhbjQiLCJ5ZWxsb3dncmVlbiIpCgpwcm9wb3J0aW9ucGxvdHMoc2V1LnEsc2V1LnZhciA9IHNldSRHZW5vdHlwZSwgc2V1LmxhYmxlID0gc2V1JENlbGxUeXBlLCBncm91cHMgPSAiR2Vub3R5cGUiLCBteV9jb2xvdXJzID0gY29sb3VycykKCmBgYAoKYGBge3J9CnZhci5saXN0IDwtIGxpc3Qoc2V1JERheXNpbkN1bHR1cmUsc2V1JEJhdGNoLHNldSRFeERhdGUsc2V1JEdlbm90eXBlKQp2YXJuYW1lcyA8LSBjKCJEYXlzIGluIEN1bHR1cmUiLCAiQmF0Y2giLCAiRXhwZXJpbWVudCBEYXRlIiwgIkdlbm90eXBlIikKIyBwbG90IGFsbCB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0IGF0IG9uY2UKCnBsb3Rwcm9wb3J0aW9ucyhzZXUsIHZhci5saXN0ID0gdmFyLmxpc3QsIHhncm91cCA9IHNldSRDZWxsVHlwZSwgdmFybmFtZXMgPSB2YXJuYW1lcywgbXlfY29sb3VycyA9IGMoImJsdWUiLCJyZWQiKSkKCgoKCmBgYAoKCkhlYXRtYXBzCgpgYGB7cn0KCiMgbWFrZSBzdXJlIHRoZSBvcmRlciBpcyBjb3JyZWN0CmNlbGx0eXBlcyA8LSBjKCJ1bmtub3duIiwicmFkaWFsIGdsaWEgMSIsICJhc3Ryb2N5dGVzIDEiLCAibWl4ZWQiLCJuZXVyb25zIDEiLAogICAgICAgICAgICAgICAibmV1cm9ucyAyIiwgImVwaXRoZWxpYWwiLCAiYXN0cm9jeXRlcyBtYXR1cmUiLCAibnBjIiwgInJhZGlhbCBnbGlhIDIiLAogICAgICAgICAgICAgICAicmFkaWFsIGdsaWEgMyIsICJlbmRvdGhlbGlhbCIsIm5ldXJvbnMgMyIsICJhc3Ryb2N5dGVzIDIiLAogICAgICAgICAgICAgICAib2xpZ29kZW5kcm9jeXRlcyIsICJzdGVtLWxpa2UgMSIsIm5ldXJhbCBzdGVtIikKCnBsb3RtZWFuKHBsb3RfdHlwZSA9ICdoZWF0bWFwJyxzZXUgPSBzZXUsIGdyb3VwID0gJ0NlbGxUeXBlJywgbWFya2VycyA9IEFCLCAKICAgICAgICAgICAgICAgICAgICAgdmFyX25hbWVzID0gY2VsbHR5cGVzLCBzbG90ID0gJ3NjYWxlLmRhdGEnLCB4bGFiID0gIkNlbGwgVHlwZSIsCiAgICAgICAgIHlsYWIgPSAiQW50aWJvZHkiKQoKCmBgYApgYGB7cn0KIyBkb3QgcGxvdAoKb2cub3JkZXIgPC0gYygidW5rbm93biIsInJhZGlhbCBnbGlhIDEiLCAiYXN0cm9jeXRlcyAxIiwgIm1peGVkIiwibmV1cm9ucyAxIiwKICAgICAgICAgICAgICAgIm5ldXJvbnMgMiIsICJlcGl0aGVsaWFsIiwgImFzdHJvY3l0ZXMgbWF0dXJlIiwgIm5wYyIsICJyYWRpYWwgZ2xpYSAyIiwKICAgICAgICAgICAgICAgInJhZGlhbCBnbGlhIDMiLCAiZW5kb3RoZWxpYWwiLCJuZXVyb25zIDMiLCAiYXN0cm9jeXRlcyAyIiwKICAgICAgICAgICAgICAgIm9saWdvZGVuZHJvY3l0ZXMiLCAic3RlbS1saWtlIDEiLCJuZXVyYWwgc3RlbSIpCgojIG1ha2Ugc3VyZSB0aGUgdGVybXMgYXJlIGV4YWN0bHkgdGhlIHNhbWUgYW5kIHlvdSBkb24ndCBtaXNzIGFueQpuZXcub3JkZXIgPC0gYygiYXN0cm9jeXRlcyAxIiwiYXN0cm9jeXRlcyAyIiwiYXN0cm9jeXRlcyBtYXR1cmUiLAogICAgICAgICAgICAgICAiZW5kb3RoZWxpYWwiLCJlcGl0aGVsaWFsIiwgIm1peGVkIiwibmV1cm9ucyAxIiwKICAgICAgICAgICAgICAgIm5ldXJvbnMgMiIsIm5ldXJvbnMgMyIsIm5ldXJhbCBzdGVtIiwibnBjIiwKICAgICAgICAgICAgICAgIm9saWdvZGVuZHJvY3l0ZXMiLAogICAgICAgICAgICAgICAicmFkaWFsIGdsaWEgMSIsInJhZGlhbCBnbGlhIDIiLCJyYWRpYWwgZ2xpYSAzIiwic3RlbS1saWtlIDEiLAogICAgICAgICAgICAgICAidW5rbm93biIpCm5ldy5vcmRlciA8LSByZXYobmV3Lm9yZGVyKQoKQUIub3JkZXIgPC0gYygiQ0QyNCIsIkNENTYiLCJDRDI5IiwiQ0QxNSIsIkNEMTg0IiwiQ0QxMzMiLCJDRDcxIiwiQ0Q0NCIsIkdMQVNUIiwiQVFQNCIsIkhlcGFDQU0iLCAiQ0QxNDBhIiwiTzQiKQoKcGxvdG1lYW4ocGxvdF90eXBlID0gJ2RvdHBsb3QnLHNldSA9IHNldSwgZ3JvdXAgPSAnQ2VsbFR5cGUnLCBtYXJrZXJzID0gQUIsIAogICAgICAgICAgICAgICAgICAgICB2YXJfbmFtZXMgPSBjZWxsdHlwZXMsIHNsb3QgPSAnc2NhbGUuZGF0YScsIHhsYWIgPSAiQ2VsbCBUeXBlIiwKICAgICAgICAgeWxhYiA9ICJBbnRpYm9keSIsIHZhcjFvcmRlciA9IG5ldy5vcmRlciwgdmFyMm9yZGVyID0gQUIub3JkZXIpCgoKYGBgCgpTdGF0aXN0aWNzIGNvbXBhcmluZyBncm91cHMKCgpgYGB7cn0KCiMgcHJlcGFyZSBhIGRhdGFmcmFtZSBmb3Igc3RhdHMKIyB0aGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBhbm5vdGF0ZWQgc2V1cmF0IG9iamVjdCB3aXRoIGFsbCB0aGUgdmFyaWFibGVzIGFscmVhZHkgZXhpc3RpbmcgYXMgbWV0YWRhdGEgc2xvdHMKCiMgY2hlY2sgd2hhdCBtZXRhIGRhdGEgc2xvdHMgZXhpc3QgaW4geW91ciBvYmplY3QKY29sbmFtZXMoc2V1QG1ldGEuZGF0YSkKCgoKYGBgCgoKYGBge3J9CiMgcnVuIHRoZSBmdW5jdGlvbgoKdmFyLm5hbWVzIDwtIGMoIkRheXNpbkN1bHR1cmUiLCAiQmF0Y2giLCAiRXhEYXRlIiwgIkdlbm90eXBlIiwgIkNlbGxUeXBlIikKCmRmLmZvci5zdGF0cyA8LSBQcmVwX2Zvcl9zdGF0cyhzZXUsIG1hcmtlcl9saXN0ID0gQUIsIHZhcmlhYmxlcyA9IHZhci5uYW1lcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXJfbmFtZSA9ICdNYXJrZXInKQoKCiMgc2F2ZSB0aGUgY3N2IGZvciBsYXRlcgp3cml0ZS5jc3YoZGYuZm9yLnN0YXRzLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvRmxvd0N5dG9tZXRyeS9QaGVub0lEL0FuYWx5c2lzL3Rlc3RpbmdMaWJyYXJ5L2RmNHN0YXRzRmViMjYuY3N2IikKCmhlYWQoZGYuZm9yLnN0YXRzKQoKCmBgYAoKCgoKCgoKCgoKCgoKCgoKCgpUcmFpbiBSYW5kb20gRm9yZXN0IG1vZGVsClJlcXVpcmVzIGEgbGFiZWxsZWQgc2V1cmF0IG9iamVjdApNb3JlIGNlbGxzIHdpbGwgZ2l2ZSBoaWdoZXIgYWNjdXJhY3kgYnV0IGluY3JlYXNlIGNvbXB1dGF0aW9uIHRpbWUuClJ1biBpbiBIUEMgd2l0aCBsb3RzIG9mIGNlbGxzCgoKYGBge3J9CgpyZiA8LSBSRk1fdHJhaW4oc2V1cmF0ZV9vYmplY3QgPSBzZXUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFCX2xpc3QgPSBBQiwgYW5ub3RhdGlvbnMgPSBzZXUkQ2VsbFR5cGUzLAogICAgICAgICAgICAgICAgICAgICAgc3BsaXQgPSBjKDAuOCwwLjIpLAogICAgICAgICAgICAgICAgICAgICAgZG93bnNhbXBsZSA9IDIwMDAwLAogICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IDIyMiwKICAgICAgICAgICAgICAgICAgICAgIG15dHJ5ID0gYygxOjEwKSwKICAgICAgICAgICAgICAgICAgICAgIG1heG5vZGVzID0gYygxMjogMjUpLAogICAgICAgICAgICAgICAgICAgICAgdHJlZXMgPSBjKDI1MCwgNTAwLCAxMDAwLDIwMDApLAogICAgICAgICAgICAgICAgICAgICAgc3RhcnRfbm9kZSA9IDE1KQoKc2F2ZShyZiwgb3V0cHV0X3BhdGgsInRyYWluZWRSRk1GZWIxNC5SZHMiKQoKYGBgCgoK